/**
 * 版权所有(C)，上海海鼎信息工程股份有限公司，2009，所有权利保留。
 * 
 * 项目名：	rumba-widget2
 * 文件名：	RNumberBox.java
 * 模块说明：	
 * 修改历史：
 * Feb 1, 2009 - lxm - 创建。
 */
package com.hd123.rumba.gwt.widget2.client.form;

import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.FocusEvent;
import com.google.gwt.event.dom.client.FocusHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.event.dom.client.KeyPressHandler;
import com.google.gwt.i18n.client.NumberFormat;
import com.hd123.gwt.types.JsBigDecimal;
import com.hd123.rumba.gwt.widget2.client.fielddef.FieldDef;
import com.hd123.rumba.gwt.widget2.client.fielddef.IntFieldDef;
import com.hd123.rumba.gwt.widget2.client.fielddef.IntegerFieldDef;
import com.hd123.rumba.gwt.widget2.client.fielddef.JsBigDecimalFieldDef;
import com.hd123.rumba.gwt.widget2.client.fielddef.StringFieldDef;

/**
 * 数值输入框控件基类。
 * <p>
 * 
 * <b>控件元素结构</b><br>
 * div@super<br>
 * ..label@super<br>
 * ..div class=field@super<br>
 * ....input type=text class=input<br>
 * ....[? class=buttons]@super<br>
 * ....[div class=suffix]@super<br>
 * ....[? class=tip]@super<br>
 * ....[div class=note]@super
 * <p>
 * 
 * <b>CSS Style Rules</b> <ol type=disc>
 * <li>.rb-NumberBox { div }</li>
 * <li>.rb-NumberBox-disabled { div }</li>
 * <li>.rb-NumberBox .input {}</li>
 * <li>.rb-NumberBox .input-disabled { 当不可用状态时的输入框 }</li>
 * <li>.rb-NumberBox .input-readonly { 当只读状态时的输入框 }</li>
 * <li>.rb-NumberBox .input-focused { 当获得焦点状态时的输入框 }</li>
 * <li>.rb-NumberBox .input-error { 当提示错误时的输入框 }</li>
 * <li>.rb-NumberBox .input-warning { 当提示警告时的输入框 }</li>
 * <li>.rb-NumberBox .buttons {  }</li>
 * <li>.rb-NumberBox .suffix { div }</li>
 * <li>.rb-NumberBox .note {  }</li> </ol>
 * 
 * @author lxm
 * 
 */
public class RNumberBox extends RTextBoxBase implements HasNumberFormat {

  private static final String STYLENAME_DEFAULT = "rb-NumberBox";

  /**
   * 创建对象实例。
   */
  public RNumberBox() {
    super(new RTextInput());
    messages = new Messages(this);
    setStyleName(STYLENAME_DEFAULT);
    //    Listener_this listener = new Listener_this();
    //    addKeyboardListener(listener);
    //    addFocusListener(listener);
    Handler handler = new Handler();
    addKeyPressHandler(handler);
    addFocusHandler(handler);
    addBlurHandler(handler);
  }

  /**
   * @param fieldDef
   *          字段定义
   */
  public RNumberBox(FieldDef fieldDef) {
    this();
    setFieldDef(fieldDef);
  }

  private int scale = -1;
  private Double value = null;
  private Number maxValue;
  private Number minValue;
  private NumberFormat format;
  private Messages messages;

  /** 小数点后的位数，默认为-1，-1表示不限制 */
  public int getScale() {
    return scale;
  }

  /**
   * 设置小数点后的位数
   * 
   * @param scale
   *          -1表示不限制
   */
  public void setScale(int scale) {
    if (scale < 0 && scale != -1)
      throw new RuntimeException("属性scale(" + scale + ")必须大于或等于0或等于-1。");
    this.scale = scale;
  }

  /** 是否允许负数，默认为false。 */
  public boolean isAllowNegative() {
    return minValue == null || minValue.doubleValue() < 0;
  }

  /** 最大值。 */
  public Number getMaxValue() {
    return maxValue;
  }

  public void setMaxValue(Number maxValue) {
    this.maxValue = maxValue;
  }

  /** 最小值。 */
  public Number getMinValue() {
    return minValue;
  }

  public void setMinValue(Number minValue) {
    this.minValue = minValue;
  }

  /** 数值显示格式化。 */
  public NumberFormat getFormat() {
    return format;
  }

  public void setFormat(NumberFormat format) {
    this.format = format;
  }

  /**
   * 取得文本输入框允许的最大输入长度（字符数）。
   * 
   * @return
   */
  public int getMaxLength() {
    return super.getMaxLength();
  }

  /**
   * 设置文本输入框允许的最大输入长度（字符数）。
   * 
   * @param length
   */
  public void setMaxLength(int length) {
    super.setMaxLength(length);
  }

  /**
   * 取消maxLength的设置
   * 
   */
  public void removeMaxLength() {
    super.removeMaxLength();
  }

  /** 提示信息对象。 */
  public Messages getMessages() {
    return messages;
  }

  /**
   * 取得当前取值。
   * 
   * @return
   */
  public Double getValue() {
    return value;
  }

  /**
   * 将当前值转换为Integer类型返回
   * 
   * @return
   */
  public Integer getValueAsInteger() {
    if (value == null)
      return null;
    return new Integer(new Long(Math.round(getValue().doubleValue())).intValue());
  }

  /**
   * 将当前值转换为JsBigDecimal类型返回
   * 
   * @return
   */
  public JsBigDecimal getValueAsJsBigDecimal() {
    if (value == null)
      return null;
    return new JsBigDecimal(getValue().toString(), scale);
  }

  /**
   * 设置当前取值。
   * 
   * @param value
   */
  public void setValue(double value) {
    if (scale != -1)
      value = roundTo(value, scale);
    this.value = new Double(value);
    setText(doubleToString(this.value.doubleValue()));
  }

  /**
   * 设置当前取值。
   * 
   * @param value
   */
  public void setValue(Double value) {
    if (value == null) {
      clearValue();
    } else {
      double d = value.doubleValue();
      if (scale != -1)
        d = roundTo(d, scale);
      this.value = new Double(d);
      setText(doubleToString(d));
    }
  }

  /**
   * 设置当前取值。
   * 
   * @param value
   */
  public void setValue(Integer value) {
    if (value == null) {
      clearValue();
      return;
    }
    setValue(value.doubleValue());
  }

  /**
   * 设置当前取值。
   * 
   * @param value
   */
  public void setValue(int value) {
    setValue((double) value);
  }

  /**
   * 设置当前取值。
   * 
   * @param value
   */
  public void setValue(JsBigDecimal value) {
    if (value == null) {
      clearValue();
      return;
    }
    setValue(value.doubleValue());
  }

  /**
   * 清空取值。清空后getValue()将返回null。
   */
  public void clearValue() {
    value = null;
    setText("");
  }

  public void setFieldDef(FieldDef fieldDef) {
    super.setFieldDef(fieldDef);

    if (fieldDef instanceof JsBigDecimalFieldDef) {
      setScale(((JsBigDecimalFieldDef) fieldDef).getScale());
    } else if (fieldDef instanceof IntegerFieldDef || fieldDef instanceof IntFieldDef) {
      setScale(0);
    } else if (fieldDef instanceof StringFieldDef) {
      setMaxLength(((StringFieldDef) fieldDef).getMaxLength());
    }
  }

  protected void onValidate() {
    super.onValidate();

    String inputText = getText();
    if ("".equals(inputText))
      return;

    double value;
    try {
      value = stringToDouble(clearThousandSeperator(inputText));
    } catch (Exception e) {
      getTip().addError(getTipPrefix() + messages.getNanText());
      return;
    }

    if (maxValue != null) {
      if (maxValue.doubleValue() < value)
        getTip().addError(getTipPrefix() + messages.getMaxText());
    }

    if (minValue != null) {
      if (minValue.doubleValue() > value)
        getTip().addError(getTipPrefix() + messages.getMinText());
    }
  }

  protected boolean beforeInputChange() {
    boolean result = super.beforeInputChange();
    if (result) {
      try {
        String s = getText();
        double d = stringToDouble(clearThousandSeperator(s));
        if (scale != -1)
          d = roundTo(d, scale);
        value = new Double(d);
      } catch (Exception e) {
        value = null;
      }
    } else {
      value = null;
    }
    return result;
  }

  private String doubleToString(double value) {
    if (format == null)
      return String.valueOf(value);
    else
      return format.format(value);
  }

  private double stringToDouble(String text) {
    if (format == null)
      return Double.parseDouble(text);
    else
      return format.parse(text);
  }

  private String clearThousandSeperator(String text) {
    return text.replaceAll("\\,", "");
  }

  /**
   * 自身的事件处理
   * 
   * @author LiQi
   * 
   */
  private class Handler implements KeyPressHandler, FocusHandler, BlurHandler {

    private static final String BASE_CHARACTER = "1234567890.,+-";

    public void onKeyPress(KeyPressEvent event) {
      int charCode = event.getCharCode();
      if (charCode == KeyCodes.KEY_LEFT || charCode == KeyCodes.KEY_RIGHT
          || charCode == KeyCodes.KEY_UP || charCode == KeyCodes.KEY_DOWN
          || charCode == KeyCodes.KEY_ENTER || charCode == KeyCodes.KEY_TAB
          || charCode == KeyCodes.KEY_BACKSPACE)
        return;

      // 如果是BASE_CHARACTER以外的键，直接取消。
      if (BASE_CHARACTER.indexOf(charCode) < 0) {
        event.preventDefault();
        return;
      }

      if (!isAllowNegative() && charCode == '-') {
        event.preventDefault();
        return;
      }

      if (charCode == ',')
        if (format == null || format.getPattern().indexOf(charCode) < 0) {
          event.preventDefault();
          return;
        }
    }

    public void onFocus(FocusEvent event) {
      // 清除格式化信息，显示原始的数值，同时保持光标位置不变
      if (value != null && !isReadOnly()) {
        int cursorPos = getInput().getCursorPos();
        setText(getText().replaceAll(",", ""));
        getInput().setCursorPos(Math.min(cursorPos, getText().length()));
      }
    }

    public void onBlur(BlurEvent event) {
      if (!isValid())
        return;

      // 格式化显示
      if (value != null)
        setText(doubleToString(value.doubleValue()));
    }

  }

  /**
   * RNumberBox提示信息。
   * 
   * @author lxm
   * 
   */
  public static class Messages {

    Messages(RNumberBox field) {
      super();
      this.field = field;
    }

    private RNumberBox field;
    private String maxText;
    private String minText;
    private String nanText = "输入的不是数字。";

    /** 当大于最大值时显示的信息，默认为“输入值大于最大值{0}。” */
    public String getMaxText() {
      if (maxText != null)
        return maxText;
      assert field.maxValue != null;
      return "输入值大于最大值" + field.doubleToString(field.maxValue.doubleValue()) + "。";
    }

    public void setMaxText(String maxText) {
      this.maxText = maxText;
    }

    /** 当小于最小值时显示的信息，默认为“输入值小于最小值{0}。” */
    public String getMinText() {
      if (minText != null)
        return minText;
      assert field.minValue != null;
      return "输入值小于最小值" + field.doubleToString(field.minValue.doubleValue()) + "。";
    }

    public void setMinText(String minText) {
      this.minText = minText;
    }

    /** 当输入的不是数字的时候，显示的提示信息。默认为“输入的不是数字。” */
    public String getNanText() {
      return nanText;
    }

    public void setNanText(String nanText) {
      this.nanText = nanText;
    }

  }

  private static native double roundTo(double d, int scale)
  /*-{
    var x = d.toFixed(scale);
    return parseFloat(x);
  }-*/;
}
